/*
 * TuplesProcessing.cpp
 *
 *  Created on: 31/01/2014
 *      Author: root
 */

#include "TuplesProcessing.h"
#include "Tuple.h"
#include <stdlib.h>

using namespace std;

//sacar a archivo de configuraciones
#define CHECK_IDLE_COND_WAIT_TIME_SECONDS 100 //???
#define MAX_TUPLE_LENGTH_TO_SHARE 3


extern int rank;
extern DppmParams * dppm;
extern Tuple * bestSolution;
extern bool end;
extern SortedBuffer * tuples;
extern pthread_mutex_t best_solution_mutex;
extern pthread_mutex_t tuples_mutex;
extern pthread_mutex_t end_flag_mutex;
extern pthread_cond_t idle_cond;

queue<Tuple*> AlmostFinished;

void EvalSolution(Tuple * newSol){
	cout<<"EvalSolution: BEGIN, rank: "<<rank<<endl;
	pthread_mutex_lock(&best_solution_mutex);

	if(!bestSolution->empty()){
	//	//cout<<"TuplesProcessing: optimalSolution(old,new): ( "<<oldSol->cost()<<" , " <<newSol->cost()<<" )"<<endl;
	}

	if (!bestSolution->empty() && ( bestSolution->cost() <= newSol->cost() )){
		delete newSol;
	}else{
		delete bestSolution;
		bestSolution = newSol;
	}

	pthread_mutex_unlock(&best_solution_mutex);
	cout<<"EvalSolution: END, rank: "<<rank<<endl;
}

//esta funcion aplica la poda
bool canBeOpt(Tuple * cand){
	//cout<<"canBeOpt: BEGIN, rank: "<<rank<<endl;
	pthread_mutex_lock(&best_solution_mutex);
	Tuple bestSolCopy = *(bestSolution->copy());
	pthread_mutex_unlock(&best_solution_mutex);

	if (bestSolCopy.empty()){
		return true;
	}


	int timeMin = cand->times(cand->lastAct());
	int time = 0;
	int timeMode = 0;

	int costMin = cand->cost();
	int cost = 0;
	int costMode = 0;
	int * times = NULL;
	int * costs = NULL;

	for(int i = cand->lastAct(); i < dppm->getNumActivities(); i++){
		//obtengo el tiempo minimo posible para el resto de las activades sin asignar
		// y me fijo si se pasa del deadline
		for(int j = 0; j < dppm->getActivityNumModes(i); j++){
			times = dppm->getActivityModesTimes(i);
			costs = dppm->getActivityModesCosts(i);
			timeMode = times[j];
			costMode = costs[j];
			time = ((time == 0)||(time <= timeMode)) ? time : timeMode;
			cost = ((cost == 0)||(cost <= costMode)) ? cost : costMode;

		}
		timeMin += time;
		costMin += cost;
		if ((timeMin > dppm->getDeadline())||(!bestSolCopy.empty() && (bestSolCopy.cost() <= costMin)))
			return false;

	}
	cout<<"canBeOpt: END, rank: "<<rank<<endl;
	return (bestSolCopy.cost() > costMin);
}



void ProcessTupleStep(Tuple * &T){


	//int countActs = GetNumActivities(dppmParms);
	cout<<"ProcessTupleStep: BEGIN, rank: "<<rank<<endl;
	int numModes = dppm->getActivityNumModes(T->lastAct() + 1);
	Tuple * T2;
	cout<<"ProcessTupleStep: actv: "<<T->lastAct()+1<<" numModes: "<<numModes<<", rank: "<<rank<<endl;
	for(int i = 0 ; i < numModes; i++){
		cout<<"ProcessTupleStep:103 i: "<<i<<", rank: "<<rank<<endl;
		T2 = T->copy();
		cout<<"ProcessTupleStep:105 i: "<<i<<",  rank: "<<rank<<endl;
		T2->sigAct();
		cout<<"ProcessTupleStep:107 i: "<<i<<",  rank: "<<rank<<endl;
		T2->modes(T2->lastAct(),i);
		cout<<"ProcessTupleStep:109 i: "<<i<<",  rank: "<<rank<<endl;
		//calculo el tiempo y el costo que insume la nueva tupla
		//obtiene el tiempo maximo que demoran sus predecesores

		int time = dppm->getPredsMaxTime(T2);
		cout<<"ProcessTupleStep:114 i: "<<i<<",  rank: "<<rank<<endl;
		//obtengo el tiempo que insume la actividad T2->now en el modo i y se lo sumo
		int timeMode = (dppm->getActivityModesTimes(T2->lastAct()))[i];
		cout<<"ProcessTupleStep:117 i: "<<i<<",  rank: "<<rank<<endl;
		time += timeMode;
		cout<<"ProcessTupleStep:119 i: "<<i<<",  rank: "<<rank<<endl;
		//el tiempo de la tupla es el ma
		T2->times(T2->lastAct(),time);
		cout<<"ProcessTupleStep:122 i: "<<i<<",  rank: "<<rank<<endl;
		T2->time( (T2->time() <= time) ? time : T2->time() );
		cout<<"ProcessTupleStep:124 i: "<<i<<",  rank: "<<rank<<endl;

		//sumo el costo del modo de la nueva actividad

		T2->cost( T2->cost() + dppm->getActivityModesCosts(T2->lastAct())[i] );
		cout<<"ProcessTupleStep:129 i: "<<i<<",  rank: "<<rank<<endl;
		if( T2->isSolution() && T2->time() <= dppm->getDeadline()){
			//Compara T2 con bestSolution y en caso de que sea mejor actualiza bestSolution
			EvalSolution(T2);
		}else{
			if((T2->times(T2->lastAct()) <= dppm->getDeadline()) && canBeOpt(T2)){

				if(dppm->getNumActivities() - T2->lastAct() < MAX_TUPLE_LENGTH_TO_SHARE){
					tuples->push(T2);
				}else{
					AlmostFinished.push(T2);
				}

			}else{
				delete T2;
			}
		}
	}//for
	delete T;
	T = NULL;
	cout<<"ProcessTupleStep: END, rank: "<<rank<<endl;
}



int TuplesProcessing(){

	//MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	cout<<"TuplesProcessing: Comenzando TuplesProcessing, rank = "<<rank<<endl;
	bool exitCondition = false;

	Tuple * tuple = NULL;
	//cout<<"TuplesProcessing: Entrando al do-while TuplesProcessing, rank = "<<rank<<endl;
	do{

		if( AlmostFinished.empty() ){
			cout<<"TuplesProcessing: \tEs vacio AlmostFinished, rank "<<rank<<endl;
			pthread_mutex_lock(&tuples_mutex);
			cout<<"TuplesProcessing: \tlock tuples_mutex, rank "<<rank<<endl;
			//Si tengo un prefijo para procesar
			if((tuple = tuples->pop()) != NULL){
				cout<<"TuplesProcessing: \tTengo tuplas para procesar, rank "<<rank<<endl;
				ProcessTupleStep(tuple);
				cout<<"TuplesProcessing: \tBEFORE delete tuple, rank "<<rank<<endl;
				//delete tuple; tuple = NULL;
				cout<<"TuplesProcessing: \tAFTER delete tuple, rank "<<rank<<endl;

			}else{
				cout<<"TuplesProcessing: \tNO!!! Tengo tuplas para procesar, rank "<<rank<<endl;
				struct timespec ts;
				ts.tv_sec = time(NULL) + CHECK_IDLE_COND_WAIT_TIME_SECONDS;

				cout<<"TuplesProcessing: TuplesProcessing: WAIT!! rank: "<<rank<<endl;
				do{
					sleep(1);
					pthread_mutex_lock(&end_flag_mutex);
					exitCondition = end;
					pthread_mutex_unlock(&end_flag_mutex);
					//pthread_cond_wait(&idle_cond,&tuples_mutex);
				}while(!exitCondition && pthread_cond_timedwait(&idle_cond,&tuples_mutex,&ts) == ETIMEDOUT);
						//pthread_cond_timedwait(&idle_cond,&tuples_mutex,&ts)

			}

			pthread_mutex_unlock(&tuples_mutex);

		}else{

			tuple = AlmostFinished.front();
			AlmostFinished.pop();
			ProcessTupleStep(tuple);
			cout<<"TuplesProcessing: \tBEFORE delete tuple, rank "<<rank<<endl;
			//delete tuple; tuple = NULL;
			cout<<"TuplesProcessing: \tAFTER delete tuple, rank "<<rank<<endl;
		}

	}while(!exitCondition);


	cout<<"TuplesProcessing: END, rank: "<<rank<<endl;
	return 0;

}



